

#include "../common/capabilities.h"
#include "../base_op/base_op.h"
#include "ldac_decode.h"
#include "ldac_dec_priv_lib.h"
#include "ldac_buffer_control.h"

#include "../../components/platform/pl_assert.h"
#include "../a2dp_decode/a2dp_common_decode.h"
#include "../common/op_msg_helpers.h"

#include "../../components/patch/patch.h"
#include "ldac_decode_struct.h"
/****************************************************************************
Private Constant Definitions
*/


/****************************************************************************
Private Type Definitions
*/

typedef struct
{
    /** A2DP_DECODER_PARAMS must be the first parameters always */
    A2DP_DECODER_PARAMS    decoder_data;

    /** The ldac_codec specific data */
    LDAC_DEC_PARAMS codec_data;

    /** Timer ID used to kick operator to ensure data keeps flowing */
    tTimerId self_kick_timer;

    LDAC_STATISTICS ldac_statistics_reporting;
} LDAC_DEC_OP_DATA;


/****************************************************************************
Private Constant Declarations
*/
#define LDAC_DECODE_PRIVATE_ID  0xC002 /* CHANGE THIS VALUE TO THAT SELECTED */

/** The stub capability function handler table */
const handler_lookup_struct ldac_decode_handler_table =
{
    ldac_decode_create,           /* OPCMD_CREATE */
    ldac_decode_destroy,          /* OPCMD_DESTROY */
    ldac_decode_start,           /* OPCMD_START */
    base_op_stop,                /* OPCMD_STOP */
    ldac_decode_reset,            /* OPCMD_RESET */
    ldac_decode_connect,         /* OPCMD_CONNECT */
    a2dp_decode_disconnect,      /* OPCMD_DISCONNECT */
    ldac_decode_buffer_details,  /* OPCMD_BUFFER_DETAILS */
    a2dp_decode_get_data_format, /* OPCMD_DATA_FORMAT */
    a2dp_decode_get_sched_info   /* OPCMD_GET_SCHED_INFO */
};

/** ldac decode capability data */
const opmsg_handler_lookup_table_entry ldac_decode_opmsg_obpm_handler_table[] =
{
    {OPMSG_COMMON_ID_GET_CAPABILITY_VERSION, base_op_opmsg_get_capability_version},
    {OPMSG_COMMON_ID_FADEOUT_ENABLE,         a2dp_dec_opmsg_enable_fadeout},
    {OPMSG_COMMON_ID_FADEOUT_DISABLE,        a2dp_dec_opmsg_disable_fadeout},
    {OPMSG_COMMON_ID_SET_CONTROL,            ldac_dec_opmsg_obpm_set_control},
    {OPMSG_COMMON_ID_GET_PARAMS,             ldac_dec_opmsg_obpm_get_params},
    {OPMSG_COMMON_ID_GET_DEFAULTS,           ldac_dec_opmsg_obpm_get_defaults},
    {OPMSG_COMMON_ID_SET_PARAMS,             ldac_dec_opmsg_obpm_set_params},
    {OPMSG_COMMON_ID_GET_STATUS,             ldac_dec_opmsg_obpm_get_status},
    {0, NULL}
};

const CAPABILITY_DATA ldac_decode_cap_data =
{
    LDAC_DECODE_PRIVATE_ID,
    LDAC_DECODE_VERSION_MAJOR, 1, /* Version information - hi and lo parts */
    1, 2,                           /* Max 1 sink and 2 sources */
    &ldac_decode_handler_table,
    ldac_decode_opmsg_obpm_handler_table,
    ldac_decode_process_data,        /* Data processing function */
    0,                              /* TODO: this would hold processing time information */
    sizeof(LDAC_DEC_OP_DATA)
};

// intermediate buffers used by LDAC decoder for storing decoded data before its written to capability
// output cbuffers
unsigned char left_out_pcm[PCM_OUTPUT_BUFFER_SIZE];
unsigned char right_out_pcm[PCM_OUTPUT_BUFFER_SIZE];

/***************************************************************************
Private Function Declarations
*/

static LDAC_DEC_OP_DATA *get_instance_data(OPERATOR_DATA *op_data)
{
    return (LDAC_DEC_OP_DATA *) base_op_get_instance_data(op_data);
}


/**
 * \brief timer handler to allow internal self-kick
 * This prevents a stall when buffers fill up
 *
 * \param timer_data Pointer to the operator instance data.
 */
void ldac_decode_timer_task(void *timer_data)
{
    OPERATOR_DATA *op_data = (OPERATOR_DATA*) timer_data;
    LDAC_DEC_OP_DATA *ldac_dec_op_data = get_instance_data(op_data);
    ldac_dec_op_data->self_kick_timer = TIMER_ID_INVALID;
    /* Raise a bg int to process */
    opmgr_kick_operator(op_data);
}

/**
 * \brief Frees up any data that has been allocated by the instance of the
 * ldac_decode capability.
 *
 * \param op_data Pointer to the operator instance data.
 */
static void free_data(OPERATOR_DATA *op_data)
{
    LDAC_DEC_OP_DATA *ldac_data = get_instance_data(op_data);
    /* This can be called when create fails or on destroy so it checks that
     * every allocation has happened before calling free. */
    /* The variables aren't reset to NULL as this is either a failed create or
     * a destroy which means the operator data is about to be freed.
     */
    /* free the shared codec data */
    mem_table_free((void *)(ldac_data->codec_data.hData),ldac_malloc_table3, LDAC_TABLE_LENGTH);

    /* free non-shared memory */
    L2_DBG_MSG1("LDAC free_data - ldac_data->codec_data == %x",(int)(&(ldac_data->codec_data)));

    mem_table_free((void *)(&(ldac_data->codec_data)), ldac_dec_malloc_table,
                                                 LDAC_TABLE_LENGTH);

}

/**
 * \brief Frees up any data that has been allocated by the instnace of the
 * ldac_decode capability and sets the response field to failed.
 *
 * \param op_data Pointer to the operator instance data.
 * \param response Pointer to the response message to give a failed status
 */
static void free_data_and_fail(OPERATOR_DATA *op_data, void **response)
{
    /* Free the data and then override the response message status to fail */
    LDAC_DEC_OP_DATA *ldac_dec_op_data = get_instance_data(op_data);
    if( ldac_dec_op_data->codec_data.cb_stream != NULL )
      cbuffer_destroy(ldac_dec_op_data->codec_data.cb_stream);
    if( ldac_dec_op_data->codec_data.RxBuf.carried != NULL )
      cbuffer_destroy(ldac_dec_op_data->codec_data.RxBuf.carried);
    if( ldac_dec_op_data->codec_data.RxBuf.main != NULL )
      cbuffer_destroy(ldac_dec_op_data->codec_data.RxBuf.main);
    if( ldac_dec_op_data->codec_data.RxBuf.clone != NULL )
      cbuffer_destroy_struct(ldac_dec_op_data->codec_data.RxBuf.clone);
    
#ifdef ENABLE_BUFFER_CONTROL
    bc_mem_destroy(&ldac_dec_op_data->codec_data.hBufCtrl);
#endif
    free_data(op_data);
    base_op_change_response_status(response, STATUS_CMD_FAILED);
}

/**
 * \brief Allocates the ldac_decode specific capability memory and initialises
 * the decoder.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the create request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */

bool ldac_decode_create(OPERATOR_DATA *op_data, void *message_data,
                                unsigned *response_id, void **response_data)
{ 
    LDAC_DEC_OP_DATA *ldac_data = get_instance_data(op_data);

    
    /* call base_op create, which also allocates and fills response message */
    if (!base_op_create(op_data, message_data, response_id, response_data))
    {
        return FALSE;
    }

    ldac_data->self_kick_timer = TIMER_ID_INVALID;
    ldac_data->codec_data.cb_stream = NULL;
    ldac_data->codec_data.RxBuf.carried = NULL;
    ldac_data->codec_data.RxBuf.main = NULL;
    ldac_data->codec_data.RxBuf.clone = NULL;
    
    /* create a new buffer */
    ldac_data->codec_data.cb_stream = cbuffer_create_with_malloc((MAX_LDAC_FRAME_SIZE+3)/4+1, BUF_DESC_LOCAL_MMU_BUFFER);
    if (ldac_data->codec_data.cb_stream == NULL)
    {
        L2_DBG_MSG("LDAC cbuffer_create_with_malloc fail-3");
        return FALSE;
    }

#ifdef ENABLE_BUFFER_CONTROL
    if(bc_mem_create(&ldac_data->codec_data.hBufCtrl)==FALSE){
        L2_DBG_MSG("LDAC cbuffer_create_with_malloc fail-4");
        return FALSE;
    }
#endif

    ldac_data->codec_data.RxBuf.carried = cbuffer_create_with_malloc(LDAC_RXBUF_CARRIED_SIZE/4, BUF_DESC_LOCAL_MMU_BUFFER);
    if (ldac_data->codec_data.RxBuf.carried == NULL)
    {
        L2_DBG_MSG("LDAC cbuffer_create_with_malloc fail-5");
        return FALSE;
    }
    /* create a new buffer */
    ldac_data->codec_data.RxBuf.main = cbuffer_create_with_malloc((LDAC_RXBUF_MAIN_SIZE/4), BUF_DESC_LOCAL_MMU_BUFFER);

    if (ldac_data->codec_data.RxBuf.main == NULL)
    {
        L2_DBG_MSG("LDAC cbuffer_create_with_malloc fail-6");
        return FALSE;
    }
    // clone codec_data.RxBuf.main
    ldac_data->codec_data.RxBuf.clone = cbuffer_create(ldac_data->codec_data.RxBuf.main->base_addr,
                                                cbuffer_get_size_in_words(ldac_data->codec_data.RxBuf.main),
                                                ldac_data->codec_data.RxBuf.main->descriptor);
    if (ldac_data->codec_data.RxBuf.clone == NULL)
    {
        L2_DBG_MSG("LDAC cbuffer_create_with_malloc fail-7");
        return FALSE;
    }
    
    /* Stage 1 - Malloc the ldac decoder fields. A lot of this can be shared
     * between any other ldac decoders/encoders in the system. Including WBS
     * capabilities. */
    /* Share memory with decoders. */

	ldac_data->codec_data.hData = (HANDLE_LDAC_BT)g_ldacbt_handle;

    /* now allocate the non-shareable memory */
    if(!mem_table_zalloc((uintptr_t *)(&(ldac_data->codec_data)), ldac_dec_malloc_table,
                                                LDAC_TABLE_LENGTH))//LDAC_DEC_MALLOC_TABLE_LENGTH))
    {
        L2_DBG_MSG("LDAC cbuffer_create_with_malloc fail-9");
        free_data_and_fail(op_data, response_data);
        return FALSE;
    }
    /* Now reserve the scratch memory */
    if (scratch_register())
    {
        if (mem_table_scratch_tbl_reserve(&decoder_scratch_table))
        {
            /* Successfully allocated everything! */
            /* Stage 2 populate the DECODER structure */
            /* Tell the codec structure where to find the ldac codec data */
            ldac_data->decoder_data.codec.decoder_data_object = &(ldac_data->codec_data);
            /*clear data*/
            ldac_data->codec_data.remain_byte=0;
            ldac_data->codec_data.remain_byte_flg=0;
             return TRUE;
        }
        /* Fail free all the scratch memory we reserved */
        scratch_deregister();
    }
    /* Clear up all the allocated memory. */
    free_data_and_fail(op_data, response_data);
    return TRUE;
}

/**
 * \brief Reports the buffer requirements of the requested capability terminal
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the buffer size request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */

 bool ldac_decode_buffer_details(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    return a2dp_decode_buffer_details_core(op_data, message_data,response_id, response_data,A2DP_DECODE_INPUT_BUFFER_SIZE,LDAC_OUTPUT_BUFF_LENGTH, LDAC_OUTPUT_BUFF_LENGTH);
}


/**
 * \brief Deallocates the ldac_decode specific capability memory.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the destroy request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */
bool ldac_decode_destroy(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    LDAC_DEC_OP_DATA *ldac_dec_op_data = get_instance_data(op_data);
    timer_cancel_event_atomic(&ldac_dec_op_data->self_kick_timer);
    
    
    
    L2_DBG_MSG("LDAC decode: ldac_decode_destroy");
    /* check that we are not trying to destroy a running operator */
    if (opmgr_op_is_running(op_data))
    {
        /* We can't destroy a running operator. */
        return base_op_build_std_response_ex(op_data, STATUS_CMD_FAILED, response_data);
    }
    else if(base_op_destroy(op_data, message_data, response_id, response_data))
    {

        if( ldac_dec_op_data->codec_data.cb_stream != NULL )
          cbuffer_destroy(ldac_dec_op_data->codec_data.cb_stream);
        if( ldac_dec_op_data->codec_data.RxBuf.carried != NULL )
          cbuffer_destroy(ldac_dec_op_data->codec_data.RxBuf.carried);

        if( ldac_dec_op_data->codec_data.RxBuf.main != NULL )
          cbuffer_destroy(ldac_dec_op_data->codec_data.RxBuf.main);
        if( ldac_dec_op_data->codec_data.RxBuf.clone != NULL )
          cbuffer_destroy_struct(ldac_dec_op_data->codec_data.RxBuf.clone);
#ifdef ENABLE_BUFFER_CONTROL
        bc_mem_destroy(&ldac_dec_op_data->codec_data.hBufCtrl);
#endif

        /* Free all the scratch memory we reserved */
        scratch_deregister();
        /* Clear up the ldac_decode specific work and then let base_op do
         * the grunt work. */
        free_data(op_data);
        return TRUE;
    }
    return FALSE;
}

/**
 * \brief Starts the ldac_decode capability so decoding will be attempted on a
 * kick.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the start request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */
bool ldac_decode_start(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    LDAC_DEC_OP_DATA *ldac_dec_op_data = get_instance_data(op_data);

    ldac_decoder_initiliase(&ldac_dec_op_data->codec_data);
    
    return a2dp_decode_start(op_data, message_data, response_id, response_data);
}


/**
 * \brief Resets the ldac_decode capability.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the reset request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */
bool ldac_decode_reset(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    if (!base_op_reset(op_data, message_data, response_id, response_data))
    {
        return FALSE;
    }
    return TRUE;
}


 /**
 * \brief Connects a capability terminal to a buffer.
 *
 * \param op_data Pointer to the operator instance data.
 * \param message_data Pointer to the connect request message
 * \param response_id Location to write the response message id
 * \param response_data Location to write a pointer to the response message
 *
 * \return Whether the response_data field has been populated with a valid
 * response
 */
bool ldac_decode_connect(OPERATOR_DATA *op_data, void *message_data,
                                    unsigned *response_id, void **response_data)
{
    return a2dp_decode_connect(op_data, message_data,response_id, response_data);
}



/**
 * \brief Reports the buffer requirements of the requested capability terminal
 *
 * \param op_data Pointer to the operator instance data.
 * \param touched Structure to return the terminals which this operator wants kicked
 */

void ldac_decode_process_data(OPERATOR_DATA *op_data, TOUCHED_TERMINALS *touched)
{
    LDAC_DEC_OP_DATA *ldac_data;
    bool input_full = FALSE;
    int nFrmRx;
    int nFrmToDec=0;
    TIME now;
    patch_fn(ldac_decode_process_data);
    ldac_data = get_instance_data(op_data);

    timer_cancel_event_atomic(&ldac_data->self_kick_timer);

    /* Check the input hasn't gone away, if it has then nothing we can do. It's
     * a radio link so this can happen */
    if (   (NULL == ldac_data->decoder_data.codec.in_buffer)
        || (NULL == ldac_data->codec_data.RxBuf.main)
        || (NULL == ldac_data->codec_data.RxBuf.clone)
        || (NULL == ldac_data->codec_data.cb_stream)
           )
    {
        /* One option is to produce silence in this case.*/
        goto LDAC_DEC_PROCESS_DATA_END;
    }
    if (!cbuffer_calc_amount_space_in_words(ldac_data->decoder_data.codec.in_buffer))
    {
        input_full = TRUE;
    }

    if( ldac_decoder_stores_rx_data(ldac_data->decoder_data.codec.in_buffer, &ldac_data->codec_data, &nFrmRx) != CODEC_SUCCESS ){
        L2_DBG_MSG("[W] LDAC decode: some received data discarded");
        ldac_data->ldac_statistics_reporting.input_data_discarded++;
    }

    touched->sinks = TOUCHED_SINK_0;

    /* reset decode status */
    ldac_data->decoder_data.codec.mode = CODEC_SUCCESS;

    /* Get number of frame to decode */
    now = time_get_time();

    // Get number of frames of LDAC encoded data to process from the input buffer
#ifdef PCM_ADJUST_MORE
    nFrmToDec = ldac_decoder_get_nFrames_to_decode( &ldac_data->codec_data,(void*)&ldac_data->decoder_data, now, 10 );
#else
    nFrmToDec = ldac_decoder_get_nFrames_to_decode( &ldac_data->codec_data, now, 10 );
#endif

    // do nothing if no complete frames of data are available for processing
    if(nFrmToDec==0){
        ldac_data->ldac_statistics_reporting.kicks_with_no_data++;
        goto LDAC_DEC_PROCESS_NODATA_TO_DECODE;
    }

    // update the performance statistics value */
    if(nFrmToDec>ldac_data->ldac_statistics_reporting.max_frames_processed_per_kick)
        ldac_data->ldac_statistics_reporting.max_frames_processed_per_kick = nFrmToDec;

    do
    {
        // reset intermediate buffer pointers
        ldac_data->codec_data.p_pcm_out_0 = &left_out_pcm[0];
        ldac_data->codec_data.p_pcm_out_1 = &right_out_pcm[0];

        // check whether there are enough output space
        if (cbuffer_calc_amount_space_in_words(ldac_data->decoder_data.codec.out_left_buffer) < ldac_data->codec_data.frame_length_sample)
        {
            ldac_data->codec_data.timer.remained_us += nFrmToDec * ldac_data->codec_data.frame_length_us;
            // update statistics reporting
            ldac_data->ldac_statistics_reporting.output_buffer_full_no_process++;
            L4_DBG_MSG("LDAC decode output buffer full");
            break;
        }
        if (cbuffer_calc_amount_space_in_words(ldac_data->decoder_data.codec.out_right_buffer) < ldac_data->codec_data.frame_length_sample)
        {
            ldac_data->codec_data.timer.remained_us += nFrmToDec * ldac_data->codec_data.frame_length_us;
            break;
        }

        // decode full frame
        ldac_data->decoder_data.codec.mode =
            ldac_decoder_decode_frames(&ldac_data->codec_data, ldac_data->codec_data.p_pcm_out_0, ldac_data->codec_data.p_pcm_out_1);

        if (ldac_data->decoder_data.codec.mode != CODEC_SUCCESS){
            // update statistics reporting
            ldac_data->ldac_statistics_reporting.decode_data_error++;
            // determine cause of decoder falure
            if(ldac_data->codec_data.playing == FALSE){ // maybe no data in rx_buf
                L2_DBG_MSG("LDAC stream underflow");
                break;
            }
            else{
                L2_DBG_MSG1("LDAC decode error mode=%d",ldac_data->decoder_data.codec.mode);
                ldac_decoder_initiliase(&ldac_data->codec_data);
                break; 
            }
        }

        // update capablity stored values
        ldac_data->decoder_data.codec.num_output_samples = ldac_data->codec_data.num_output_samples;
        ldac_data->decoder_data.codec.num_input_octets_consumed = ldac_data->codec_data.frame_size;

        // one frame processed, reduce frames to process count
        nFrmToDec--;

        // if frame successfully decoded, copy to output CBuffers and process metadata
        if (ldac_data->decoder_data.codec.num_output_samples > 0)
        {
            static int last_samples=0;
            unsigned num_chans;
            /* Source 0 is always touched */
            unsigned touched_sources = TOUCHED_SOURCE_0;
            tCbuffer fadeout_buffer;

            //copy decoded data to capability output CBuffers
            cbuffer_write(ldac_data->decoder_data.codec.out_left_buffer, (int*)ldac_data->codec_data.p_pcm_out_0, ldac_data->decoder_data.codec.num_output_samples);
            cbuffer_write(ldac_data->decoder_data.codec.out_right_buffer, (int*)ldac_data->codec_data.p_pcm_out_1, ldac_data->decoder_data.codec.num_output_samples);
            
#ifdef INSTALL_METADATA
            // The metadata frames of the incoming LDAC data have already been removed by the process of copying to
            // an intermediate buffer but a metadata tag must be created for the data copied to the capability output buffer
            // to ensure data continues to flow. the timestamp information of the incoming data is now lost as this is discard
            // in the ldac_decode_private copying of the incoming data to an intermediate buffer and the use of Sony ttp scheme

            // debug print to show when frame size/bit rate changes
            if(last_samples != ldac_data->decoder_data.codec.num_input_octets_consumed)
            {
                L2_DBG_MSG2("LDAC metadata process ip samples = %d op frame_size = %d",ldac_data->decoder_data.codec.num_input_octets_consumed,ldac_data->decoder_data.codec.num_output_samples);
                // update statistics field
                ldac_data->ldac_statistics_reporting.bit_rate_changed++;
            }
            last_samples = ldac_data->decoder_data.codec.num_input_octets_consumed;

            // check that the output data already has metadata tags applied to it, otherwise need to create new tag
            if (buff_has_metadata(ldac_data->decoder_data.metadata_op_buffer))
            {
                unsigned after_octets = ldac_data->decoder_data.codec.num_output_samples * OCTETS_PER_SAMPLE;

                // create metadata tag
                metadata_tag *mtag = buff_metadata_new_tag();

                // no existing tag, create new tag, one per frame of data
                // validate new metadata tag
                if (mtag != NULL)
                {
                    mtag->length = ldac_data->decoder_data.codec.num_output_samples * OCTETS_PER_SAMPLE;
                    METADATA_PACKET_START_SET(mtag);
                    METADATA_PACKET_END_SET(mtag);
                    buff_metadata_append(ldac_data->decoder_data.metadata_op_buffer, mtag, 0, after_octets);
                }
            }

#endif      /* INSTALL_METADATA */

            /* Is fadeout enabled? if yes, do it on the new output data */
            if ( (ldac_data->decoder_data.left_fadeout.fadeout_state != NOT_RUNNING_STATE))
            {
                /* If it is enabled it is enabled on both channels. Call it on left and
                 * right. They should both fadeout at the same rate so if one says
                 * finished then they both should be and we tell the host. */

                /* A small optimisation here, if there is only a mono output connected
                 * then we only need to call fade out on the left channel, which is
                 * why we tested the left state above. If left is fading out it is
                 * definitely connected so we always fade it out. By fading out the
                 * right channel first (iff it is connected) we don't need to think
                 * about it's response or when to send the fade out done message.
                 */
                if (ldac_data->decoder_data.codec.out_right_buffer != NULL)
                {
                    fadeout_buffer = *ldac_data->decoder_data.codec.out_right_buffer;
                    fadeout_buffer.read_ptr = ldac_data->decoder_data.codec.out_right_buffer->write_ptr;
                    mono_cbuffer_fadeout(&fadeout_buffer,
                                        ldac_data->decoder_data.codec.num_output_samples, &(ldac_data->decoder_data.right_fadeout));
                }
                fadeout_buffer = *ldac_data->decoder_data.codec.out_left_buffer;
                fadeout_buffer.read_ptr = ldac_data->decoder_data.codec.out_left_buffer->write_ptr;
                if (mono_cbuffer_fadeout(&fadeout_buffer,
                                        ldac_data->decoder_data.codec.num_output_samples, &(ldac_data->decoder_data.left_fadeout)))
                {
                    common_send_simple_unsolicited_message(op_data, OPMSG_REPLY_ID_FADEOUT_DONE);
                }
            }

           /* If stereo outputs are connected then source 1 was touched as well */
           if (ldac_data->decoder_data.codec.out_right_buffer != NULL)
           {
               touched_sources |= TOUCHED_SOURCE_1;
               num_chans = 2;
           }
           else
           {
               num_chans = 1;
           }
           /* Source(s) touched */
           touched->sources = touched_sources;

           NOT_USED(num_chans);
        }
    }while ((nFrmToDec>0) && (ldac_data->decoder_data.codec.mode == CODEC_SUCCESS));

#ifdef ENABLE_BUFFER_CONTROL
#endif

LDAC_DEC_PROCESS_NODATA_TO_DECODE:

    if (input_full && cbuffer_calc_amount_space_ex(ldac_data->codec_data.RxBuf.main))
    {
        /**
         * If the input buffer was full, it is likely that the endpoint could not
         * copy data into the transform buffer. To solve this problem, LDAC decode will kick the endpoint
         * after having consumed from a full buffer. In cases where the endpoint should kick an
         * upstream entity this is useful so the upstream entity can be notified of more space.
         * In other cases it helps to get data into the decoder as quickly as possible.
         */
        touched->sinks |= TOUCHED_SINK_0;
    }


LDAC_DEC_PROCESS_DATA_END:
    timer_schedule_event_in_atomic(LDAC_DEC_SELF_KICK_RATIO * stream_if_get_system_kick_period(),
                                 ldac_decode_timer_task, (void*)op_data, &ldac_data->self_kick_timer);
    return;
}

#define LDAC_CTRL_PARAMS_ID_SF 0xA0
#define LDAC_CTRL_PARAMS_ID_CM 0xA1
bool ldac_dec_opmsg_obpm_set_control(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
    
    LDAC_DEC_OP_DATA *ldac_dec_op_data = get_instance_data(op_data);

    CPS_CONTROL_SOURCE  cntrl_src;
    unsigned  num_controls, i, cntrl_id, cntrl_value;
    OPMSG_RESULT_STATES result = OPMSG_RESULT_STATES_NORMAL_STATE;

    if (opmgr_op_is_running(op_data))
    {
        return FALSE;
    }

    result = OPMSG_RESULT_STATES_NORMAL_STATE;

    patch_fn(ldac_dec_opmsg_obpm_set_control_patch);
    
    if(!cps_control_setup(message_data, resp_length, resp_data, &num_controls))
    {
       return FALSE;
    }

    for(i=0; i<num_controls; i++)
    {
        cntrl_id = cps_control_get(message_data, i, &cntrl_value, &cntrl_src);

        if(cntrl_id == LDAC_CTRL_PARAMS_ID_SF)
        {
            ldac_dec_op_data->codec_data.sampling_freq = cntrl_value;
        }
        else if(cntrl_id == LDAC_CTRL_PARAMS_ID_CM)
        {
            ldac_dec_op_data->codec_data.channel_mode = cntrl_value;
        }
        else {
            result = OPMSG_RESULT_STATES_INVALID_PARAMETER;
        }
    }

    ldac_dec_op_data->codec_data.frame_length_sample = 128;
    switch (ldac_dec_op_data->codec_data.sampling_freq)
    {
      case 192000:
        ldac_dec_op_data->codec_data.frame_length_sample <<= 1;
      case  96000:
        ldac_dec_op_data->codec_data.frame_length_sample <<= 1;
      case  48000:
#ifdef PCM_ADJUST
        ldac_dec_op_data->codec_data.frame_length_us = 2666;//trial for pcm adjust
        ldac_dec_op_data->codec_data.adjust_frm_num = LDAC_PCM_ADJUST_COUNT_48K;
#else
        ldac_dec_op_data->codec_data.frame_length_us = 2667;
#endif
        break;
      case 176400:
        ldac_dec_op_data->codec_data.frame_length_sample <<= 1;
      case  88200:
        ldac_dec_op_data->codec_data.frame_length_sample <<= 1;
      case  44100:
        ldac_dec_op_data->codec_data.frame_length_us = 2902;
#ifdef PCM_ADJUST
        ldac_dec_op_data->codec_data.adjust_frm_num = LDAC_PCM_ADJUST_COUNT_44K;
#endif
        break;
      default:
        result = OPMSG_RESULT_STATES_INVALID_PARAMETER;
    }
    L2_DBG_MSG1("set control  sampling_freq=%d",ldac_dec_op_data->codec_data.sampling_freq);

    if( result == OPMSG_RESULT_STATES_NORMAL_STATE ) {
        HANDLE_LDAC_BT hData;
        int cm, nshift, sf, ldac_result;

        
       hData = ldac_dec_op_data->codec_data.hData;
       cm    = ldac_dec_op_data->codec_data.channel_mode;
       ldacBT_get_handle(hData,cm);
        
        
        hData = ldac_dec_op_data->codec_data.hData;
        cm    = ldac_dec_op_data->codec_data.channel_mode;
        sf    = ldac_dec_op_data->codec_data.sampling_freq;
        nshift= 0;
        ldac_result = ldacBT_init_handle_decode( hData, cm, sf, nshift, 0, 0 );
        if(ldac_result){
            result = OPMSG_RESULT_STATES_INVALID_PARAMETER;
        }
    }

    cps_response_set_result(resp_data, result);

    return TRUE;
}


bool ldac_dec_opmsg_obpm_get_params(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
    return FALSE;
}

bool ldac_dec_opmsg_obpm_get_defaults(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
  
     return FALSE;
}

bool ldac_dec_opmsg_obpm_set_params(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
    /* Set the parameter(s). For future proofing, it is using the whole mechanism, although currently there is only one field
     * in opdata structure that is a setable parameter. If later there will be more (ever), must follow contiquously the first field,
     * as commented and instructed in the op data definition. Otherwise consider moving them into a dedicated structure.
     */

    return FALSE;
}

bool ldac_dec_opmsg_obpm_get_status(OPERATOR_DATA *op_data, void *message_data, unsigned *resp_length, OP_OPMSG_RSP_PAYLOAD **resp_data)
{
    LDAC_DEC_OP_DATA * op_extra_data = get_instance_data(op_data);

    unsigned* resp = NULL;
    
    if(!common_obpm_status_helper(message_data,resp_length,resp_data,sizeof(LDAC_DECODE_STATISTICS) ,&resp))
    {
         return FALSE;
    }

    if(resp)  
    {    
        resp = cpsPack2Words(op_extra_data->codec_data.sampling_freq, op_extra_data->codec_data.channel_mode, resp);
        cpsPack1Word(op_extra_data->codec_data.frame_size,resp);
    }
    return TRUE;
}

